# ~*~ coding: utf-8 ~*~
import json

from sqlalchemy.sql.sqltypes import TEXT, VARCHAR
from sqlalchemy.dialects.mysql import MEDIUMTEXT

from apps.common.utils import get_signer


class JsonTypeMixin:

    type = None

    @property
    def python_type(self):
        return self.type

    def bind_processor(self, dialect):
        impl_processor = super().bind_processor(dialect)
        dumps = json.dumps
        if impl_processor:

            def process(value):
                if not isinstance(value, self.type):
                    raise TypeError('Column type must be {}'.format(self.type))
                if value is not None:
                    value = dumps(value)
                return impl_processor(value)

        else:

            def process(value):
                if not isinstance(value, self.type):
                    raise TypeError('Column type must be {}'.format(self.type))
                if value is not None:
                    value = dumps(value)
                return value

        return process

    def result_processor(self, dialect, coltype):
        impl_processor = super().result_processor(dialect, coltype)
        loads = json.loads
        if impl_processor:

            def process(value):
                value = impl_processor(value)
                if value is None:
                    return None
                return loads(value)

        else:

            def process(value):
                if value is None:
                    return None
                return loads(value)

        return process

    def compare_values(self, x, y):
        return x == y


class JsonDictCharField(JsonTypeMixin, VARCHAR):
    type = dict


class JsonDictTextField(JsonTypeMixin, TEXT):
    type = dict


class JsonDictMediumTextField(JsonTypeMixin, MEDIUMTEXT):
    type = dict


class JsonListCharField(JsonTypeMixin, VARCHAR):
    type = list


class JsonListTextField(JsonTypeMixin, TEXT):
    type = list


class JsonListMediumTextField(JsonTypeMixin, MEDIUMTEXT):
    type = list

signer = get_signer()


class EncryptJsonTypeMixin:
    type = None

    @property
    def python_type(self):
        return self.type

    def bind_processor(self, dialect):
        impl_processor = super().bind_processor(dialect)
        dumps = json.dumps
        if impl_processor:

            def process(value):
                if not isinstance(value, self.type):
                    raise TypeError('Column type must be {}'.format(self.type))
                if value is not None:
                    value = signer.sign(dumps(value))
                return impl_processor(value)

        else:

            def process(value):
                if not isinstance(value, self.type):
                    raise TypeError('Column type must be {}'.format(self.type))
                if value is not None:
                    value = signer.sign(dumps(value))
                return value

        return process

    def result_processor(self, dialect, coltype):
        impl_processor = super().result_processor(dialect, coltype)
        loads = json.loads
        if impl_processor:

            def process(value):
                value = impl_processor(value)
                if value is None:
                    return None
                return loads(signer.unsign(value))

        else:

            def process(value):
                if value is None:
                    return None
                return loads(signer.unsign(value))

        return process

    def compare_values(self, x, y):
        return x == y


class EncryptJsonDictCharField(EncryptJsonTypeMixin, VARCHAR):
    type = dict


class EncryptJsonListCharField(EncryptJsonTypeMixin, VARCHAR):
    type = list


class EncryptJsonDictTextField(EncryptJsonTypeMixin, TEXT):
    type = dict


class EncryptJsonListTextField(EncryptJsonTypeMixin, TEXT):
    type = list


class EncryptTypeMixin:

    def bind_processor(self, dialect):
        impl_processor = super().bind_processor(dialect)
        if impl_processor:

            def process(value):
                if value is not None:
                    value = signer.sign(value)
                return impl_processor(value)

        else:

            def process(value):
                if value is not None:
                    value = signer.sign(value)
                return value

        return process

    def result_processor(self, dialect, coltype):
        impl_processor = super().result_processor(dialect, coltype)
        if impl_processor:

            def process(value):
                value = impl_processor(value)
                if value is None:
                    return None
                return signer.unsign(value)

        else:

            def process(value):
                if value is None:
                    return None
                return signer.unsign(value)

        return process

    def compare_values(self, x, y):
        return x == y


class EncryptTextField(EncryptTypeMixin, TEXT):
    pass


class EncryptCharField(EncryptTypeMixin, VARCHAR):
    pass
